package com.metaaps.eoclipse;

import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.media.opengl.GL;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.glu.GLU;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.part.ViewPart;

import com.metaaps.eoclipse.api.GeoContext;
import com.metaaps.eoclipse.api.GeometricLayer;
import com.metaaps.eoclipse.api.IImageLayer;
import com.metaaps.eoclipse.api.ILayerListener;
import com.metaaps.eoclipse.api.ILayerManager;
import com.metaaps.eoclipse.api.ILayerUser;
import com.metaaps.eoclipse.io.VectorIO;
import com.metaaps.eoclipse.layers.FastImageLayer;
import com.metaaps.eoclipse.layers.LayerManager;
import com.metaaps.eoclipse.layers.SimpleVectorLayer;
import com.metaaps.eoraster.EOImageReaderFactory;

public class ImageView extends ViewPart implements ILayerUser{

	public static final String ID = "EOclipse.view";
	private GLCanvas canvas;
	private LayerManager root=new LayerManager(null);
	private GeoContext geocontext=new GeoContext();
	protected int dyy;
	protected int dxx;
	private ArrayList<ILayerListener> listeners=new ArrayList<ILayerListener>();

	public void createPartControl(final Composite parent) {
		Composite top = new Composite(parent, SWT.NONE);
		top.setLayout(new FillLayout());
		GLData data = new GLData();
		data.doubleBuffer = true;
		canvas = new GLCanvas(top, SWT.NONE, data);
		canvas.setCurrent();
		final GLContext context = GLDrawableFactory.getFactory().createExternalGLContext();
		context.makeCurrent();
		GL gl = context.getGL();
		geocontext.initialize(context);
		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		context.release();
		
		canvas.addListener(SWT.Resize, new Listener() {
			public void handleEvent(Event event) {
				Rectangle bounds = canvas.getBounds();
				canvas.setCurrent();
				context.makeCurrent();
				GL gl = context.getGL ();
				geocontext.setHeight(bounds.height);
				geocontext.setWidth(bounds.width);
				gl.glViewport(0, 0, bounds.width, bounds.height);
		        gl.glMatrixMode(GL.GL_PROJECTION);
		        gl.glLoadIdentity();
		        new GLU().gluOrtho2D(0, 1, 0, 1);
		        gl.glMatrixMode(GL.GL_MODELVIEW);
		        gl.glLoadIdentity();
				context.release();
			}
		});
		
		canvas.addMouseMoveListener(new MouseMoveListener() {
			
			@Override
			public void mouseMove(MouseEvent e) {
                if (e.stateMask==SWT.SHIFT) {
                    dxx = (int) (geocontext.getZoom() * (e.x - canvas.getBounds().width / 2) / 10);
                    dyy = (int) (geocontext.getZoom() * (e.y - canvas.getBounds().height / 2) / 10);
                    geocontext.setDirty(true);
                } else {
                    Point p = new Point();
                    try {
                        p.x = (int) (geocontext.getX() + e.x * geocontext.getWidth() / canvas.getBounds().width * geocontext.getZoom());
                        p.y = (int) (geocontext.getY() + e.y * geocontext.getHeight() / canvas.getBounds().height * geocontext.getZoom());
                        root.mouseMoved(p, geocontext);
                    } catch (Exception ex) {
                    }
                }
            }

            
        });

		
		 canvas.addMouseWheelListener(new MouseWheelListener() {
			
			@Override
			public void mouseScrolled(MouseEvent e) {
			
	                float zoom = (float) (geocontext.getZoom() * Math.pow(2, -e.count / 20.0));
	                
	                // check mouse position
	                int posX = e.x;
	                int posY = e.y;
	                // calculate the image position of the current position of the mouse
	                int x = (int) (geocontext.getX() + posX * geocontext.getWidth() / canvas.getBounds().width * geocontext.getZoom());
	                int y = (int) (geocontext.getY() + posY * geocontext.getHeight() / canvas.getBounds().height * geocontext.getZoom());
	                geocontext.setZoom(zoom);
	                // translate the image origin to have the same mouse position in the geocontext
	                geocontext.setX((int) (x - posX * geocontext.getWidth() / canvas.getBounds().width * geocontext.getZoom()));
	                geocontext.setY((int) (y - posY * geocontext.getHeight() / canvas.getBounds().height * geocontext.getZoom()));
	                geocontext.setDirty(true);
	            }
	        });
		
		final Runnable core=new Runnable() {
			int count=0;
			long[] times=new long[500];
			public void run() {
				if (!canvas.isDisposed()) {
					if(count==times.length){
						count=0;
						double fps=0;
						for(int i=0;i<times.length-1;i++){
							fps+=(times[i+1]-times[i])/1000.;
						}
						fps/=times.length-1;
						System.out.println(1/fps);
					}else{
						times[count++]=System.currentTimeMillis();
					}
					canvas.setCurrent();
					context.makeCurrent();
					GL gl = context.getGL();
					gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
					if (dxx != 0 || dyy != 0) {
						dxx /= 1.2;
                        dyy /= 1.2;
			            geocontext.setX(geocontext.getX() + dxx);
			            geocontext.setY(geocontext.getY() + dyy);
			        }
					geocontext.initialize(context);
					geocontext.setDirty(true);
					root.render(geocontext);
					canvas.swapBuffers();
					context.release();
					try {
						Thread.sleep(15);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					canvas.getDisplay().asyncExec(this);
				}
			}
		};
		canvas.getDisplay().asyncExec(core);
		
	}
	
	public void addImage(String file){
		IImageLayer iil=new FastImageLayer(root, EOImageReaderFactory.create(file));
		root.addLayer(iil);
		Map config=new HashMap();
		config.put("url","/Users/gershwinou/NetBeansProjects/GeoImageViewer/src/org/geoimage/viewer/core/resources/shapefile/Global GSHHS Land Mask.shp");
		iil.addLayer(new SimpleVectorLayer("Global GSHHS Land Mask.shp", iil, GeometricLayer.POLYGON, VectorIO.createVectorIO(VectorIO.SIMPLE_SHAPEFILE, config, iil.getImage()).read()));
	}

	static void drawTorus(GL gl, float r, float R, int nsides, int rings) {
		float ringDelta = 2.0f * (float) Math.PI / rings;
		float sideDelta = 2.0f * (float) Math.PI / nsides;
		float theta = 0.0f, cosTheta = 1.0f, sinTheta = 0.0f;
		for (int i = rings - 1; i >= 0; i--) {
			float theta1 = theta + ringDelta;
			float cosTheta1 = (float) Math.cos(theta1);
			float sinTheta1 = (float) Math.sin(theta1);
			gl.glBegin(GL.GL_QUAD_STRIP);
			float phi = 0.0f;
			for (int j = nsides; j >= 0; j--) {
				phi += sideDelta;
				float cosPhi = (float) Math.cos(phi);
				float sinPhi = (float) Math.sin(phi);
				float dist = R + r * cosPhi;
				gl.glNormal3f(cosTheta1 * cosPhi, -sinTheta1 * cosPhi, sinPhi);
				gl.glVertex3f(cosTheta1 * dist, -sinTheta1 * dist, r * sinPhi);
				gl.glNormal3f(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
				gl.glVertex3f(cosTheta * dist, -sinTheta * dist, r * sinPhi);
			}
			gl.glEnd();
			theta = theta1;
			cosTheta = cosTheta1;
			sinTheta = sinTheta1;
		}
	}

	public ILayerManager getRootLayer(){
		return root;
	}
	
	
	@Override
	public void setFocus() {
		canvas.setFocus();
	}
	
	@Override
	public void dispose() {
		canvas.dispose();
		super.dispose();
	}

	@Override
	public void addListenner(ILayerListener l) {
		this.root.addListenner(l);
	}

	@Override
	public void removeListenner(ILayerListener l) {
		this.root.removeListenner(l);
	}
}
